﻿using Core.DataAccess.Model.iTool.Gateway;
using Core.Framework;
using Core.Framework.Common;
using Dashboard.Models;
using Environment.Interfaces;
using HandlerContextOptions;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Primitives;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Dashboard.Middlewares
{
    public class NodeServiceMiddlewares
    {
        private readonly RequestDelegate _next;
        private INodeService iNodeService;
        private IToolCenter iToolCenter;

        const string gatewaydatabasePath = "../gatewaydatabase.config";
        const string localIp = "127.0.0.1";

        public NodeServiceMiddlewares(RequestDelegate next, INodeService iNodeService, IToolCenter iToolCenter)
        {
            this._next = next ?? throw new ArgumentNullException(nameof(next));
            this.iNodeService = iNodeService;
            this.iToolCenter = iToolCenter;
        }


        public async Task Invoke(HttpContext context)
        {
            bool isWebSocket = context.WebSockets.IsWebSocketRequest;

            if (isWebSocket)
            {
                await this._next(context);
                return;
            }

            if (context.Request.Headers.ContainsKey("publicip"))
            {
                context.Request.Headers.TryGetValue("publicip", out StringValues publicip);

                publicip = AppSetting.GatewayPublicIp == publicip ? localIp : publicip;

                if (string.IsNullOrWhiteSpace(publicip))
                {
                    context.Response.StatusCode = 886;
                    await context.Response.WriteAsync(string.Empty);
                    return;
                }

                if (context.Request.Headers.ContainsKey("seed"))
                {
                    context.Request.Headers.TryGetValue("seed", out StringValues seed);

                    string desKey = Md5Helper.Hash(AppSetting.ApplicationSeedString + publicip);

                    // 验证身份
                    if (publicip != DESEncrypt.Decrypt(seed, desKey))
                    {
                        context.Response.StatusCode = 885;
                        await context.Response.WriteAsync(string.Empty);
                        return;
                    }
                }

                else if (context.Request.Path.Value != "/nodestatus")
                {
                    context.Response.StatusCode = 885;
                    await context.Response.WriteAsync(string.Empty);
                    return;
                }

                LoggerUtils.Write($"NodeServiceMiddlewares: {context.Request.Path.Value}", "log2");


                switch (context.Request.Path.Value)
                {
                    case "/nodestatus":
                        var nodestatus = await new StreamReader(context.Request.Body).ReadToEndAsync();

                        LoggerUtils.Write($"nodestatus: {nodestatus},{context.Request.Headers.ContainsKey("seed")}", "log2");

                        if (string.IsNullOrWhiteSpace(nodestatus) || !context.Request.Headers.ContainsKey("seed"))
                        {
                            LoggerUtils.Write($"this.GatewayPublicIp 1: {AppSetting.GatewayPublicIp},{publicip}", "log3");

                            // 请求Seed
                            if (await iNodeService.AddNode(publicip))
                            {
                                LoggerUtils.Write($"iNodeService.AddNode", "log3");

                                if (localIp == publicip)
                                {
                                    LoggerUtils.Write($"iNodeService.AddNode.SaveDatabaseAsync", "log3");
                                    await this.SaveDatabaseAsync();
                                }

                                LoggerUtils.Write($"iNodeService.AddNode.SaveDatabaseAsync end", "log3");

                                context.Response.StatusCode = 200;
                                string desKey = Md5Helper.Hash(AppSetting.ApplicationSeedString + publicip);
                                await context.Response.WriteAsync(DESEncrypt.Encrypt(publicip, desKey));
                            }
                            else
                            {
                                LoggerUtils.Write($"iNodeService.AddNode error", "log3");

                                context.Response.StatusCode = 201;
                                await context.Response.WriteAsync(string.Empty);
                            }
                            return;
                        }
                        else
                        {
                            var nodestatusmodel = nodestatus.TryToEntity<LinuxSystemStatus>();
                            if (nodestatusmodel == null)
                            {
                                // 验证失败
                                context.Response.StatusCode = 201;
                                await context.Response.WriteAsync(string.Empty);
                                return;
                            }
                            await iNodeService.ModifyStatus(publicip, nodestatusmodel);
                        }
                        break;

                    case "/processstatus":
                        var processstatus = await new StreamReader(context.Request.Body).ReadToEndAsync();
                        var processstatusmodel = processstatus.TryToEntity<LinuxProcessStatus>();
                        if (processstatusmodel?.ServiceInstanceId > 0)
                        {
                            await iNodeService.Reporting(publicip, processstatusmodel);
                        }
                        else
                        {
                            // 验证失败
                            context.Response.StatusCode = 201;
                            await context.Response.WriteAsync(string.Empty);
                            return;
                        }
                        break;

                    case "/instances":
                        context.Response.StatusCode = 200;
                        var instances = await iNodeService.GetInstances(publicip);
                        await context.Response.WriteAsync(instances.Select(item => new
                        {
                            item.ServiceInstanceId,
                            item.IsRunInDocker,
                            item.Title
                        }).TryToJson());
                        return;

                    case "/itoolconfig":
                        context.Response.StatusCode = 200;
                        var iToolConfigure = await this.iToolCenter.GetiToolConfigure();
                        await context.Response.WriteAsync(iToolConfigure == null ? string.Empty : iToolConfigure.TryToJson().Replace("127.0.0.1", AppSetting.GatewayPublicIp));
                        return;

                    case "/stop":
                        context.Response.StatusCode = 200;
                        await iNodeService.Stoped(publicip);
                        return;

                    case "/service.process":
                        context.Response.StatusCode = 200;
                        LoggerUtils.Write($"NodeServiceMiddlewares: {context.Request.Path.Value}");
                        var servicevalue = await new StreamReader(context.Request.Body).ReadToEndAsync();
                        LoggerUtils.Write($"NodeServiceMiddlewares content:  {servicevalue}");
                        var serviceStatus = (ProcessStatusEnum)Enum.Parse(typeof(ProcessStatusEnum), servicevalue);
                        await this.iNodeService.UpdateServiceStatus(publicip, serviceStatus);
                        return;

                    case "/client.process":
                        context.Response.StatusCode = 200;
                        var clientvalue = await new StreamReader(context.Request.Body).ReadToEndAsync();
                        LoggerUtils.Write($"NodeServiceMiddlewares: {context.Request.Path.Value}");
                        LoggerUtils.Write($"NodeServiceMiddlewares content:  {clientvalue}");
                        var clientStatus = (ProcessStatusEnum)Enum.Parse(typeof(ProcessStatusEnum), clientvalue);
                        await this.iNodeService.UpdateClientStatus(publicip, clientStatus);
                        return;

                    default:
                        context.Response.StatusCode = 404;
                        await context.Response.WriteAsync(string.Empty);
                        break;
                }
            }
            else
            {
                await this._next(context);
            }

        }

        private async Task SaveDatabaseAsync()
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var value = File.ReadAllText(gatewaydatabasePath);
                var dbParameter = Newtonsoft.Json.JsonConvert.DeserializeObject<CreateDataBaseRequest>(value);

                var node = await context.Nodes.Where(item => item.IP == localIp).FirstAsync();
                var instance = await context.ServiceInstances.Where(item => item.NodeIP == localIp && item.Title == "iToolGatewaySqlServer").FirstOrDefaultAsync();
                if (instance == null)
                {
                    var ServiceInstance = new ServiceInstance();
                    ServiceInstance.NodeIP = localIp;
                    ServiceInstance.Title = "iToolGatewaySqlServer";
                    ServiceInstance.NodeId = node.NodeId;
                    ServiceInstance.User = "sa";
                    ServiceInstance.Password = dbParameter.Passward;
                    ServiceInstance.Port = dbParameter.Port;
                    ServiceInstance.Type = string.Empty;
                    ServiceInstance.Status = 2;
                    ServiceInstance.IsRunInDocker = true;
                    ServiceInstance.Process = "SqlServer";
                    ServiceInstance.LastCheckTime = DateTime.UtcNow;
                    await context.ServiceInstances.AddAsync(ServiceInstance);
                    await context.SaveChangesAsync();
                }
                else
                {
                    instance.Status = 2;
                    await context.SaveChangesAsync();
                }

            }
        }



    }
}
